package com.bestvike.stone.spring.context;

import com.bestvike.stone.core.lang.StringUtils;
import com.bestvike.stone.core.threading.InheritThreadLocal;
import com.bestvike.stone.spring.controller.BaseController;
import com.bestvike.stone.spring.shiro.auth.Auth;
import org.springframework.util.Assert;

import java.util.ArrayDeque;
import java.util.Deque;

/**
 * 线程上下文
 * Created by 许崇雷 on 2017-10-19.
 */
public final class ThreadContext {
    //线程本地存储
    private static final ThreadLocal<ThreadContextData> contexts = InheritThreadLocal.withInitial(ThreadContextData::new);

    //region property

    public static boolean exists() {
        return contexts.get().exists;
    }

    public static String getToken() {
        return contexts.get().token;
    }

    public static void setToken(String token) {
        ThreadContextData data = contexts.get();
        data.token = token == null ? StringUtils.EMPTY : token;
    }

    public static <TAuth extends Auth> TAuth getAuth() {
        //noinspection unchecked
        return (TAuth) contexts.get().auth;
    }

    public static void setAuth(Auth auth) {
        ThreadContextData data = contexts.get();
        data.auth = auth;
    }

    public static <TUserData> TUserData getUserData() {
        //noinspection unchecked
        return (TUserData) contexts.get().userData;

    }

    public static <TUserData> void setUserData(TUserData userData) {
        ThreadContextData data = contexts.get();
        data.userData = userData;
    }

    public static BaseController getEntryController() {
        return contexts.get().controllerStack.peekLast();
    }

    public static BaseController getExecutingController() {
        return contexts.get().controllerStack.peekFirst();
    }

    //endregion

    public static void init(String token) {
        //可在此处添加验证
        ThreadContextData data = contexts.get();
        data.exists = true;
        data.token = token == null ? StringUtils.EMPTY : token;
        data.auth = null;
        data.userData = null;
        data.controllerStack.clear();
    }

    public static void reset() {
        ThreadContextData data = contexts.get();
        data.exists = false;
        data.token = StringUtils.EMPTY;
        data.auth = null;
        data.userData = null;
        data.controllerStack.clear();
    }

    public static void enterController(BaseController controller) {
        Assert.notNull(controller, "controller can not be null");
        contexts.get().controllerStack.push(controller);
    }

    public static void exitController() {
        contexts.get().controllerStack.pop();
    }

    private static final class ThreadContextData {
        private final Deque<BaseController> controllerStack = new ArrayDeque<>();
        private boolean exists;
        private String token;
        private Auth auth;
        private Object userData;
    }
}
